/*
 *********************************************************************************
 * Authors: narsil <ygk@ebidding.com.cn> 2014-06-27 49206C6F766520636F6E74726F6C *
 *********************************************************************************
 */
package com.exercise.module.common.file.controller;

import com.exercise.core.exception.EbsException;
import com.exercise.module.common.dao.entity.EbsFile;
import com.exercise.module.common.file.service.FileService;
import com.exercise.serlvet.controller.BaseController;
import com.google.common.base.Charsets;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

@Controller
@RequestMapping("/sys")
public class DownloadController extends BaseController {

	private static Logger logger = LoggerFactory.getLogger(DownloadController.class);
	
	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
	
	@Autowired
	@Qualifier("ftpFileService")
	private FileService fileService;
	/**
	 * titles 这个参数是组成文件夹然后打包 并且 flag=1的情况下创建文件夹    -------李洋
	 */
	@RequestMapping("/download/{fileId}")
	public void index(@PathVariable String fileId,String accessCode,String downloadType,HttpServletRequest request, HttpServletResponse response,String titles,String flag) {

		
		EbsFile ebsFile = null;
		if(StringUtils.isEmpty(fileId)) {
			throw new EbsException("fileId 不能为空");

		} else {
			if(StringUtils.isNotEmpty(accessCode)) {
				ebsFile = fileService.getFileById(fileId,accessCode);
			} else {
				ebsFile = fileService.getFileById(fileId);
			}
		}
		
		if(ebsFile == null) {
			throw new EbsException("文件不存在!");
		}
		
		/** 要下载的文件 */
		File downloadFile = ebsFile.getFile();
		
		/** 记录文件大小 */
		long fileLength = downloadFile.length();
		
		/** 录已下载文件大小 */
		long pastLength = 0;
		
		/** 0:从头开始的全文下载;1:从某字节开始的下载（bytes=8192-）;2:从某字节开始到某字节结束的下载（bytes=8192-1048576） */
		int rangeSwitch = 0;
		
		/** 记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=8192-1048576,则这个值是为1048576) */
		long toLength = 0;
		
		/** 客户端请求的字节总量 */
		long contentLength = 0;
		
		/** 记录客户端传来的形如"bytes=8192-"或者"bytes=8192-1048576"的内容 */
		String rangeBytes = "";//
		
		/** 随机读取数据 */
		RandomAccessFile raf = null;
		
		/** 写出数据*/
		OutputStream os = null;
		
		/** 缓冲*/
		OutputStream out = null;
		
		/** 暂存容器 */
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		
		/**  客户端请求的下载的文件块的开始字节 */
		if (request.getHeader("Range") != null) {
			response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
			logger.info("request.getHeader(\"Range\")=" + request.getHeader("Range"));
			rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");
			
			
			if (rangeBytes.indexOf('-') == rangeBytes.length() - 1) {/** bytes=8192- */
				rangeSwitch = 1;
				rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
				pastLength = Long.parseLong(rangeBytes.trim());
				
				/**客户端请求的是 8192 之后的字节 */
				contentLength = fileLength - pastLength + 1;
			} else {/** bytes=8192-1048576 */
				rangeSwitch = 2;
				String temp0 = rangeBytes.substring(0,rangeBytes.indexOf('-'));
				String temp2 = rangeBytes.substring(rangeBytes.indexOf('-') + 1, rangeBytes.length());
				
				/** bytes=8192-1048576,从第 8192 个字节开始下载*/
				pastLength = Long.parseLong(temp0.trim());
				
				/** bytes=8192-1048576,到第 1048576 个字节结束*/
				toLength = Long.parseLong(temp2);
				
				/**客户端请求的是 8192-1048576  之间的字节 */
				contentLength = toLength - pastLength + 1;//
			}
		} else {/** 从开始进行下载 */
			/** 客户端要求全文下载*/
			contentLength = fileLength;
		}
		
		/**
		 * 如果设设置了Content-Length，则客户端会自动进行多线程下载。如果不希望支持多线程，则不要设置这个参数。
		 * 响应的格式是:
		 * Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
		 * response().setHeader("Content-Length",new Long(file.length() - p).toString());
		 */
		/** 告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes */
		response.reset();
		
		/** 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK */
		response.setHeader("Accept-Ranges", "bytes");
		if (pastLength != 0) {
			/**不是从最开始下载,  响应的格式是:
			 * Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
			 */ 
			logger.info("不是从开始进行下载！服务器即将开始断点续传...");
			switch (rangeSwitch) {
				case 1 : {/** 针对 bytes=8192- 的请求 */
					String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-").append(new Long(fileLength - 1).toString()).append("/").append(new Long(fileLength).toString()).toString();
					response.setHeader("Content-Range", contentRange);
					break;
				} case 2 : {/** 针对 bytes=8192-1048576 的请求 */
					String contentRange = rangeBytes + "/" + new Long(fileLength).toString();
					response.setHeader("Content-Range", contentRange);
					break;
				} default : {
					break;
				}
			}
		} else {
			/** 是从开始下载 */
			logger.info("是从开始进行下载！");
		}
		
		try {
			setResponse(ebsFile.getFileName(),request,response);
	        response.addHeader("Content-Length", String.valueOf(contentLength));
			os = response.getOutputStream();
	    	out = new BufferedOutputStream(os);
	    	raf = new RandomAccessFile(downloadFile, "r");
	    	try {
	    		switch (rangeSwitch) {
		    		case 0 : {/** 普通下载，或者从头开始的下载 */ 
		    		} case 1 : {/** 针对 bytes=8192- 的请求 */
		    			raf.seek(pastLength);/** 形如 bytes=8192- 的客户端请求，跳过 8192  个字节 */
				        int n = 0;
				        while ((n = raf.read(buffer, 0, 1024)) != -1) {
				        	out.write(buffer, 0, n);
				        }
		    			break;
		    		}
		    		case 2 : {/** 针对 bytes=8192-1048576 的请求 */
		    			raf.seek(pastLength - 1);/** 形如 bytes=8192-1048576 的客户端请求，找到第 1048576 个字节 */
		    			int n = 0;
		    			
		    			/** 记录已读字节数 */
		    			long readLength = 0;
		    			while (readLength <= contentLength - DEFAULT_BUFFER_SIZE) {/** 大部分字节在这里读取 */
		    				n = raf.read(buffer, 0, DEFAULT_BUFFER_SIZE);
		    				readLength += DEFAULT_BUFFER_SIZE;
		    				out.write(buffer, 0, n);
		    			}
		    			if (readLength <= contentLength) {/** 余下的不足 1024 个字节在这里读取 */
		    				n = raf.read(buffer, 0, (int)(contentLength - readLength));
		    				out.write(buffer, 0, n);
		    			} 
		    			
		    			break;
		    		}
		    		default : {
		    			break;
		    		}
	    		}
		        out.flush();
	    	} catch(IOException ie) {
	    		/**
				 * 在写数据的时候，
				 * 对于 ClientAbortException 之类的异常，
				 * 是因为客户端取消了下载，而服务器端继续向浏览器写入数据时，
				 * 抛出这个异常，这个是正常的。
				 * 尤其是对于迅雷这种吸血的客户端软件，
				 * 明明已经有一个线程在读取 bytes=1275856879-1275877358，
				 * 如果短时间内没有读取完毕，迅雷会再启第二个、第三个。。。线程来读取相同的字节段，
				 * 直到有一个线程读取完毕，迅雷会 KILL 掉其他正在下载同一字节段的线程，
				 * 强行中止字节读出，造成服务器抛 ClientAbortException。
				 * 所以，我们忽略这种异常
				 */ 
	    	}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(out); 
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			}
		}
	}
	
	public void setResponse(String fileName,HttpServletRequest request,HttpServletResponse response) {
		
		try {
			String agent = request.getHeader("USER-AGENT"); 
			
			String contentType = null;
			String attachment = null;
			String ext = FilenameUtils.getExtension(fileName);
//	        if(StringUtils.equalsIgnoreCase("doc", ext)) {
//	        	contentType = "application/msword";
//	        	attachment = "application/msword";
//	        } else if(StringUtils.equalsIgnoreCase("xls", ext)){
//	        	contentType = "application/vnd.ms-excel";
//	        	attachment = "application/vnd.ms-excel";
//	        } else if(StringUtils.equalsIgnoreCase("pdf", ext)) {	        	
//	        	contentType = "application/pdf";
//	        	attachment = "application/pdf";
//	        } else if(StringUtils.equalsIgnoreCase("zip", ext)) {
//		    	contentType = "application/zip";
//		    	attachment = "application/zip";
//		    } else if(StringUtils.equalsIgnoreCase("rar", ext)) {
//		    	contentType = "application/zip";
//		    	attachment = "application/zip";
//		    } else 
			if(StringUtils.equalsIgnoreCase("swf", ext)) {
		    	contentType = "application/x-shockwave-flash";
		    	attachment = "application/x-shockwave-flash"; 
		    } else {
		    	contentType = "application/octet-stream";
		    	attachment = "aattachment";
		    }
	        //contentType = "application/x-msdownload";
	        //contentType = "application/octet-stream";
	    	//attachment = "aattachment";
	        if (null != agent && (-1 != agent.indexOf("MSIE") || -1 != agent.indexOf("Trident")))  {
	        	fileName = URLEncoder.encode(fileName, "utf-8"); 
	        } else {
	        	fileName = new String(fileName.getBytes(Charsets.UTF_8),Charsets.ISO_8859_1);
	        }
	        
		    response.setContentType(contentType);	
		    response.addHeader("Content-Disposition",attachment + ";filename=" + fileName);  
		} catch(Exception e) {
			throw new EbsException(e);
		}
	}
}
